Skip to content

浏览器脚本加载策略深度解析

在现代 Web 开发中,脚本的加载方式对页面的性能(如白屏时间、FCP 等)有着至关重要的影响。合理选择加载策略可以显著提升用户体验。

一、脚本加载时序图

下图展示了不同加载策略在 HTML 解析、脚本下载和脚本执行三个阶段的执行时序。

脚本加载策略时序对比图


二、核心属性对比

1. 默认情况 (Normal Script)

  • 行为: 浏览器解析 HTML 时,遇到 <script> 标签会立即停止解析,开始下载脚本。下载完成后立即执行脚本,执行完毕后再恢复解析 HTML。
  • 缺点: 如果脚本较大或网络较慢,会造成显著的解析阻塞,导致页面白屏。

2. async (异步)

  • 代码: <script async src="..."></script>
  • 下载: 异步下载,下载过程不阻塞 HTML 解析。
  • 执行: 下载完立即执行。执行脚本时会暂停 HTML 解析。
  • 顺序: 脚本的执行顺序不固定,谁先下载完谁先执行。
  • 场景: 适用于不依赖其他脚本且不被其他脚本依赖的独立脚本(如广告、统计代码)。

3. defer (延迟)

  • 代码: <script defer src="..."></script>
  • 下载: 异步下载,下载过程不阻塞 HTML 解析。
  • 执行: HTML 解析完成后执行(在 DOMContentLoaded 事件之前)。
  • 顺序: 保证执行顺序。脚本按在 HTML 中出现的顺序依次执行。
  • 场景: 现代 Web 开发的首选。适用于依赖 DOM 结构或有相互依赖关系的脚本。

4. preload (预加载)

  • 代码: <link rel="preload" href="..." as="script">
  • 行为: 告诉浏览器该资源是当前页面立即需要的,请以高优先级下载。
  • 特点: 只下载不执行。下载后会缓存在本地,后续遇到对应的 <script> 标签时直接从缓存读取。
  • 场景: 用于提前加载关键资源(如核心库、关键字体、首屏 CSS)。

5. prefetch (预读取)

  • 代码: <link rel="prefetch" href="...">
  • 行为: 告诉浏览器该资源可能是未来页面需要的,请在空闲时间低优先级下载。
  • 特点: 只下载不执行。
  • 场景: 用于加载下一个路由或页面可能需要的资源,提升后续页面的加载速度。

三、总结对比表

属性异步下载阻塞解析执行时机执行顺序优先级
Normal下载后立即执行顺序执行
async下载不阻塞,执行阻塞下载完成后立即执行谁快谁执行
deferHTML 解析完成后执行顺序执行
preload无(仅下载)-最高
prefetch无(仅下载)-最低

四、面试高频问题

1. async 和 defer 的区别是什么?

回答:

  • 相同点: 都是异步下载,下载过程都不阻塞 HTML 解析。
  • 不同点:
    • 执行时机: async 下载完立即执行;defer 要等到 HTML 解析完成后再执行。
    • 执行顺序: async 无法保证顺序(谁先下完谁执行);defer 保证按照 HTML 中的出现顺序执行。
    • 依赖性: async 适用于独立脚本;defer 适用于需要操作 DOM 或有相互依赖关系的脚本。

2. preload 和 prefetch 的区别及使用场景?

回答:

  • preload: 用于加载当前页面必须的关键资源,优先级最高。如果下载后 3 秒内未被使用,浏览器会抛出警告。
  • prefetch: 用于加载未来页面可能需要的资源,优先级最低,利用浏览器空闲时间下载。

3. 为什么建议将 <script> 放在 <body> 底部?

回答: 在没有使用 asyncdefer 的情况下,脚本会阻塞 HTML 解析。放在底部可以确保 HTML 结构先被解析和渲染,避免因为脚本加载导致的页面白屏。但在使用 defer 后,脚本放在 <head> 中也能达到类似甚至更好的性能。

4. 多个 defer 脚本会按顺序执行吗?

回答: 是的。HTML 规范要求 defer 脚本必须按照它们在文档中出现的顺序执行。

最近更新